package com.conversationboard.model;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import com.conversationboard.cache.ThreadTitleCache;
import com.conversationboard.database.Database;
import com.conversationboard.database.Queries;
import com.conversationboard.logger.Logger;

/**
 * @author Keith Watson
 */

public class Bookmarks implements Iterable<Bookmark> {

	private Map<String, Bookmark> bookmarks = new HashMap<String, Bookmark>();


	public Bookmarks() {
	}


	/**
	 * Update a bookmark (because of a moved thread) if it exists in the user's list of bookmarks.
	 * 
	 * @param oldBoardId
	 * @param oldThreadId
	 * @param newBoardId
	 * @param newThreadId
	 */

	public void updateBookmark(int oldBoardId, int oldThreadId, int newBoardId, int newThreadId) {

		String key = Bookmarks.getKey(oldBoardId, oldThreadId);

		if (bookmarks.containsKey(key)) {
			Bookmark bookmark = bookmarks.get(key);
			bookmark.setBoardId(newBoardId);
			bookmark.setThreadId(newThreadId);

			bookmarks.remove(key);

			String newKey = Bookmarks.getKey(newBoardId, newThreadId);

			bookmarks.put(newKey, bookmark);

		}
	}


	public boolean hasThreadBookmarked(int boardId, int threadId) {
		return this.bookmarks.containsKey(Bookmarks.getKey(boardId, threadId));
	}


	public int size() {
		return this.bookmarks.size();
	}


	public Iterator<Bookmark> iterator() {
		return this.bookmarks.values().iterator();
	}


	public static String getKey(int boardId, int threadId) {
		return boardId + "-" + threadId;
	}


	public void add(Bookmark bookmark) {
		String key = Bookmarks.getKey(bookmark.getBoardId(), bookmark.getThreadId());
		this.bookmarks.put(key, bookmark);
	}


	public Map getBookmarks() {
		return this.bookmarks;
	}


	public void remove(String key) {

		this.bookmarks.remove(key);
	}


	public boolean isEmpty() {
		return this.bookmarks.isEmpty();
	}


	public static Bookmarks load(User user) throws SQLException {

		Bookmarks bookmarks = new Bookmarks();

		Connection connection = null;

		try {

			connection = Database.getConnection();

			PreparedStatement statement = connection.prepareStatement(Queries.GET_BOOKMARKS);
			statement.setInt(1, user.getUserId());

			statement.execute();

			ResultSet resultSet = statement.getResultSet();

			while (resultSet.next()) {

				Bookmark bookmark = new Bookmark();

				bookmark.setBoardId(resultSet.getInt("boardid"));
				bookmark.setThreadId(resultSet.getInt("threadid"));
				bookmark.setMessageId(resultSet.getInt("messageid"));

				String title = "";

				if (ThreadTitleCache.threadExists(bookmark.getBoardId(), bookmark.getThreadId())) {
					title = ThreadTitleCache.getTitle(bookmark.getBoardId(), bookmark.getThreadId());
				} else {
					Bookmarks.delete(connection, user.getUserId(), bookmark.getBoardId(), bookmark.getThreadId());
					continue;
				}

				/* If the user can't read this board, then remove this bookmark and log it as an error */

				if (!user.canRead(bookmark.getBoardId())) {
					Bookmarks.delete(connection, user.getUserId(), bookmark.getBoardId(), bookmark.getThreadId());
					Logger.log(new Date(), "User bookmarked thread against board they can't read; has been deleted from database: ID [" + bookmark.getBoardId() + ":" + bookmark.getThreadId() + "]", user);
					continue;
				}

				/* If I can't find the title, then the thread this bookmark points to has been removed (maybe the
				 * thread has been moved). Mark it as such. */

				bookmark.setTitle(title);

				bookmarks.add(bookmark);
			}

			return bookmarks;

		} finally {
			Database.close(connection);
		}

	}


	public static void delete(Connection connection, int userId, int boardId, int threadId) throws SQLException {

		boolean standaloneTransaction = false;

		if (connection == null) {
			connection = Database.getConnection();
			standaloneTransaction = true;
		}

		try {
			PreparedStatement statement = connection.prepareStatement(Queries.DELETE_BOOKMARK);
			statement.setInt(1, userId);
			statement.setInt(2, boardId);
			statement.setInt(3, threadId);

			statement.executeUpdate();

		} finally {
			if (standaloneTransaction) {
				Database.close(connection);
			}
		}
	}


	public static void deleteAll(int userId) throws SQLException {

		Connection connection = null;

		try {

			connection = Database.getConnection();

			PreparedStatement statement = connection.prepareStatement(Queries.DELETE_ALL_BOOKMARKS);
			statement.setInt(1, userId);

			statement.executeUpdate();

			statement.close();

		} finally {
			Database.close(connection);
		}

	}

}
